// #pragma once
// #include<iostream>
// #include<string>
// #include<sys/types.h>
// #include<sys/socket.h>
// #include<strings.h>
// #include<netinet/in.h>
// #include<functional>
// #include<arpa/inet.h>
// #include<cstdio>
// #include<string.h>
// #include"Log.hpp"
// using func_t = function<string(const string&)>;

// Log log; 

// using namespace std;

// enum
// {
//     SOCK_ERR = 1,
//     BIND_ERR = 2    
// };

// //用来设置缺省值
// const uint16_t defaultport = 8080;
// const string defaultip = "123.249.103.51";
// const int size = 1024;

// class UdpServer
// {
// public://一般用户都不喜欢用4字节的整形表示ip地址。 用户都是用的点分十进制字符串风格的ip地址。 
//     UdpServer(uint16_t port = defaultport, string ip = defaultip)
//         :port_(port) 
//         ,ip_(ip)
//         ,isrunning_(false)
//     {}

//     void init()
//     {
//         //创建udp套接字
//         sockfd_ = socket(AF_INET, SOCK_DGRAM, 0);
//         if (sockfd_ < 0)
//         {
//             log(Fatal, "socket create error, sockfd: ", sockfd_);
//             exit(1);
//         }
//         log(Info, "socket create success, sockfd: %d", sockfd_);
        
//         //2.绑定端口号

//         //2.1创建套接字
//         struct sockaddr_in local;   //结构是什么？ ——一个叫做sin_port, 一个sin_addr, sin_port. 这两个都是 

//         //2.2初始化套接字
//         bzero(&local, sizeof(local));
//         //2.3将套接字进行赋值
//         local.sin_family = AF_INET; //sin_family表明我们自己的结构体类型    
//         //主机序列转网络序列， 有接口
//         local.sin_port = htons(port_);     //保证我们的端口号是网络字节序列， 因为该端口号是要给对方发送的。
//         local.sin_addr.s_addr = inet_addr(ip_.c_str());       //string -> uint32_t, 如何快速的将ip转为字符串, 就是使用inet_addr
        
//         // local.sin_addr.s_addr = INADDR_ANY;       //string -> uint32_t, 如何快速的将ip转为字符串, 就是使用inet_addr
        
//         //2.4. bind 绑定
//         //我们的端口号ip地址， 有没有向操作系统的套接字去设置。 答案是没有， 截止到现在， 我们的套接字local还是在栈上面。 地址空间的用户级栈上。 
        
//         if ((bind(sockfd_, (const struct sockaddr*)&local, sizeof(local))) < 0) 
//         {
//             log(Fatal, "bind error, errno %d, error string: %s", errno, strerror(errno));
//             exit(BIND_ERR);
//         }

//         log(Info, "bind success, error: %d, error string: %s", errno, strerror(errno));

//     }   

//     void run(func_t func)
//     {
//         isrunning_ = true;
//         char inbuffer[size];

//         while (isrunning_)
//         {
//             //能够从udp中读取数据
//             struct sockaddr_in client;
//             socklen_t clilen = sizeof(client);
//             //从客户端读取数据  
//             ssize_t n = recvfrom(sockfd_,  inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&client, &clilen);
            
//             if (n < 0)
//             {
//                 log(Waring, "recvfrom error, errno: %d, err string: %s", errno, strerror(errno));
//                 continue;
//             }
//             //
//             inbuffer[n] = 0;
//             string info = inbuffer;
//             string echo_string = func(info);  //回调函数， 对代码进行分层。 


//             sendto(sockfd_, echo_string.c_str(), echo_string.size(), 0, (struct sockaddr*)&client, clilen);

//         }

//     }

//     ~UdpServer()
//     {
//         if (sockfd_ > 0) close(sockfd_);
//     }


// private:
//     int sockfd_;  //网络文件描述符
//     uint16_t port_;   //端口号, 表明服务器的端口号
//     string ip_;       //ip地址

//     //我们的网络服务器是一直能够一直运行的
//     bool isrunning_;
// };

#include<iostream>
using namespace std;
#include<sys/types.h>
#include<string>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<string.h>
#include<strings.h>
#include"Log.hpp"
#include<functional>
#include<netinet/in.h>
#include<unordered_map>


int defaultport = 8080;
string defaultip = "0.0.0.0";

using func_t = function<string(string, sockaddr_in&, unordered_map<string, sockaddr_in>&)>; 

Log lg;

enum
{
    SockError = 2,
    BindError = 3,
    RecvError = 4,
};

class UdpServer
{
public:
    UdpServer(uint16_t port = defaultport) 
        : port_(port), ip_(defaultip), isrunning_(false)
    {}

    void Init()
    {
        //先创建套接字变量并且完成初始化。 然后就创建网卡文件
        sockaddr_in local;
        
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port_);
        local.sin_addr.s_addr = inet_addr(ip_.c_str());
        sockfd_ = socket(AF_INET, SOCK_DGRAM, 0);
        if (sockfd_ < 0)
        {
            lg(Fatal, "create sock error, errno: %d, strerror: %s", errno, strerror(errno));
            exit(SockError);
        }
        lg(Info, "create sock success");
        
        //绑定
        if (bind(sockfd_, (sockaddr*)&local, sizeof(local)) < 0) 
        {
            lg(Fatal, "bind error, errno: %d, strerror: %s", errno, strerror(errno));
            exit(BindError);
        }
        lg(Info, "bind success");
        //

    }

    void BroadCast(string message, unordered_map<string, sockaddr_in>& clients)
    {
        cout << "1 "<< endl;
        for (auto& e : clients)
        {
            sendto(sockfd_, message.c_str(), message.size(), 0, (sockaddr*)&e.second, sizeof(e.second));
        }
        cout << "2 " << endl;
    }

    
    void run(func_t func)
    {
        isrunning_ = true;
        char inbuffer[1024];


        while (isrunning_)
        {
            memset(inbuffer, 0, sizeof(inbuffer));
            sockaddr_in client;
            socklen_t client_len;
            memset(&client, 0, sizeof(client));
            //接收数据的同时监听到客户端的来源
            ssize_t s = recvfrom(sockfd_, inbuffer, sizeof(inbuffer) - 1, 0, (sockaddr*)&client, &client_len);
            if (s < 0) 
            {
                lg(Waring, "recvfrom error, errno: %d, strerror: %s", errno, strerror(errno));
                continue;
            }
            inbuffer[s] = 0;

            //处理数据
            //创建套接字， 用来监听是哪一个客户端

            string message = inbuffer;
            message = func(message, client, clients);//这里的处理使用一个外部的接口

            //处理完成后， 返回发送给客户端
            BroadCast(message, clients);

        }
    }

    ~UdpServer()
    {
        if (sockfd_ > 0) close(sockfd_);    
    }
private:
    int sockfd_;
    uint16_t port_;
    string ip_;
    bool isrunning_;
    unordered_map<string, sockaddr_in> clients;

};